<?php
/***************************************************************************

						PHP judge for windows 
	needs PsTools  http://technet.microsoft.com/en-us/sysinternals/bb896649
	
****************************************************************************/
include ("config.php");

/***************************************************************************
			
			Parametros:
			
			
					'file'   	: 	Nombre del archivo que se va a evaluar ( main.java , main.cpp , main.c )
									Esta archivo debe estar en la ruta a la que hace referencia __CODES__
					'testname'	:	Nombre de los casos de prueba con los que se evaluara, deben ser el mismo
									para testName.in y testName.out ya que el juez identifica ambos con el 
									mismo nombre. Estos archivos deben estar guardados en la ruta a la que
									hace referencia __TEST_CASES__.
					'tlimit'	:	Representa el tiempo maximo que no debe superar un script en ejecucion.
								
								
								(_ ver en el archivo config.php para todas las definiciones de rutas_)
			
			Respuesta 	: 		JSON 
						
						
						"time"			: con el tiempo en milisegundos que tardo en ejecutarse el codigo
						"line"+N		: donde N representa el numero de linea, con la respuesta que dio el 
										  codigo evaluado. 
										  
										  Ejemlo : "line0":"xxxxxxxx", "line1":"yyyyyyyy" ... "lineN":"zzzzzzzz"
										  
						"lines"			: numero de lineas de respuesta que genero el codigo evaluado.
						
						"wline"+N		: donde N representa el numero de linea, con la respuesta incorrecta 
										  del codigo evaluado comparada con el caso de prueba. 
										  
										  Ejemlo : "wline0":"incorrecta-correcta", "line1":"incorrecta-correcta" ... "lineN":"incorrecta-correcta";
						
						"wlines"		: numero de lineas incorrectas del codigo evaluado comparado con el caso de prueba. 
										  Si todo fue correcto "wlines" contendra un 0;
						"eline"+N		: cuando se producen errores al momento de compilar o ejecutar ( si el programa no exede tiempo de ejecucion )
										  "eline"+N tendra la descripcion de lo que el compilador devolvio.
										   
										  Ejemlo : "eline0":"In function `int main(int, char**)':", "line1":"xxxxxxxx" ... "lineN":"xxxxxxx";
										   
						"elines"		: numero de lineas de respuesta que devolvio el compiador
						
						"mlines"		: numero que representa la diferencia entre lineas que se obtuvieron y las que se evaluaron, el codigo genero mlines mas.
						
						"status"		: representa el status del juez, si es "ok" compilo y evaluo correctamente el codigo,
										  si es "error" ocurrio algun error y el campo "error" contendra la descripcion.
										  
										  
						"error"			: contiene la descripcion del error en caso de que haya ocurrido alguno.
						
****************************************************************************/


// 				Comienza la evaluacion del problema, deben ser especificados los 3 parametros, si no se regresara un mensaje de error.

	if( !StatusJuez() ) : // El juez no esta ocupado
	
		if( isset( $_REQUEST['file'] ) && isset( $_REQUEST['testname'] ) && isset( $_REQUEST['tlimit'] ) ):	
			
			newRun( $_REQUEST['file'] , $_REQUEST['testname'] , $_REQUEST['tlimit'] );
			
		else:
		
			echo '{"status":"error","error":"missing arguments"}';
			
		endif;
	else:
	
		echo '{ "status" : "error" , "error" : "bussy" }';
		
	endif;





 function PsExecute( $command, $timeout = 60, $sleep = 2 , $pname = "" ){
		
		pclose( popen( "start /B cmd /c ". $command ." > ".(__CODES__)."logexec.txt 2>&1", "r" ) );
		
        $cur = 0;
        
		// Second, loop for $timeout seconds checking if process is running
		
        while( $cur < $timeout ) {
		
            sleep($sleep);
			
            $cur += $sleep;			
           
		   // If process is no longer running, return true;
		   
            if( !PsExists( $pname ) ):
			
                return true; // Process must have exited, success!
				
			endif;
        }
         
        // If process is still running after timeout, kill the process and return false
		
        PsKillByName( "$pname.exe" );
		
        return false;
    }
    
   
    function PsExists( $name = "Main" ){
		
	   exec( dirname(__FILE__). "\\PsTools\\pslist.exe $name ", $output);		
	   
        foreach ($output as $line):
	   
			$pos = strpos( $line , "$name was not found" );
			
			if($pos !== false):			
				
				return false; 
				
			endif;
			
	    endforeach;
	   
        return true;
		
    }
   
	function PsKillByName($name) {
	
        exec( dirname(__FILE__). "\\PsTools\\pskill.exe $name", $output);		
		
    }
function excCmd( $cmd , $type , $time = 5 , $kill = false , $pname = ""){

	$cmd = str_replace( "/" , "\\" , $cmd );
	
	if(__DEBUG__)echo $cmd."</br>";	
	
	$out = array();
	
	$json = array();
	
	$i=0;	
	
	if( $kill ): //cuando ejecutamos el codigo posiblemente queramos matar alguna proceso que entre en ciclos infinitos o sobrepase el tiempo de ejecucion
	
		if ( !PsExecute( $cmd , $time , 1 , $pname ) ) :
			
			$json["status"] = "error";		
		
			$json["error"] = "time limit";
			
			return $json;
			
		endif;
		
	endif;
	
	
	/************************************* LOG ********************************************/
	
	//guardar resultados de error en log.txt y agregar las lineas de respuesta al json
	
	$l = 0; //numero de elines
	
	if( $type === "compilation" ):
		
		$log = $cmd." > ".(__CODES__)."log.txt 2>&1";
	
		exec( $log );
		
		$log_file = file_get_contents( (__CODES__)."log.txt" ); 
		
		$log_file = str_replace( (__CODES__) , "" , $log_file );
		
		$log_lines = explode( "\r\n" , $log_file);
		
		foreach( $log_lines as $line ):
			
			$json["eline".$l] = $line;
			
			$l++;
			
		endforeach;
	else:
		
		$log_file = file_get_contents( (__CODES__)."logexec.txt" ); 
		
		$log_file = str_replace( (__CODES__) , "" , $log_file );
		
		$log_lines = explode( "\r\n" , $log_file);
		
		foreach( $log_lines as $line ):
			
			$json["eline".$l] = $line;
			
			$l++;
			
		endforeach;		
	
	endif;
	
	$json["elines"] = $l;
	/************************************* /LOG ********************************************/
	
	$time_s  = microtime( true );
	
	$output = exec( $cmd , $out , $return );
	
	$time_e = microtime( true );
	
	$json["time"] = ( $time_e - $time_s );
	
		foreach( $out as $line ):		
		
			if(__DEBUG__)
				echo "{$line}</br>";					
			
			$json["line".$i] = $line;	
			
			$i++;
			
		endforeach;
			
			$json["lines"] = $i;	
			
			if(__DEBUG__):
			
				echo "{$output} </br> ";
			
				echo "{$return}</br>";
				
			endif;
		
	if( $return != 0 ):
	
		$json["status"] = "error";		
		
		$json["error"] = $type;
		
	endif;
	
	if( $return == 0 ):
	
		$json["status"] = "ok";		
		
		$json["error"] = "";
		
	endif;
	
	$json["type"] = $return;
	
	return $json;
}

function createJavaClass( $file_name ){

	if(__DEBUG__)
	
		echo "creando .class </br>";
		
	$cmd = ' "'.(__JAVAC_PATH__).'" '.(__CODES__).''.$file_name.'.java';	
	
	return excCmd($cmd, "compilation");
	
}

//la clase siempre debe ser Main

function execJavaClass( $testCaseName , $time ){

	if(__DEBUG__)
	
		echo "ejecutando .class </br>";
		
	$cmd = ' "'.(__JAVA_PATH__).'" -cp '.(__CODES__).' Main <  '.(__TEST_CASES__).''.$testCaseName.'.in';	
	
	$json = excCmd( $cmd , "ejecution" , $time, true , "Java" );
	
	if(file_exists((__CODES__)."Main.class")):
			
			unlink((__CODES__)."Main.class");
			
	endif;
	
	return $json;
}


function evalJavaFile( $file_name , $test_case_name , $time ){	

	$json = createJavaClass( $file_name );
	
	if( $json["error"] !== "" ):
	
		return  $json;
		
	endif;
	
	$json = execJavaClass( $test_case_name , $time );	
	
	return $json;
}

function createCppExe( $file ){

	if(__DEBUG__)
	
		echo "compilando </br>";
		
	$cmd = ' "'.(__GPP_PATH__).'" -o '.(__CODES__).'Main '.(__CODES__).''.$file.' ';		
	
	$json = excCmd( $cmd , "compilation" );	
	
	return $json ;
	
}

function createCExe( $file ){

	if(__DEBUG__)
	
		echo "compilando </br>";
		
	$cmd = ' "'.(__CGG_PATH__).'" -o '.(__CODES__).'Main '.(__CODES__).''.$file.' ';		
		
	$json = excCmd( $cmd , "compilation" );	
	
	return  $json ;
	
}

function execExeFile( $testCaseName , $time ){

	if(__DEBUG__)
	
		echo "ejecutando .exe </br>";
		
	$cmd = ' "'.(__CODES__).'Main.exe" <  '.(__TEST_CASES__).''.$testCaseName.'.in';	
	
	$json = excCmd( $cmd , "ejecution" , $time ,true , "Main" );
	
	if( file_exists( ( __CODES__ )."Main.exe" ) ):
			
			unlink( ( __CODES__ )."Main.exe" );
			
	endif;
	
	return $json;
}

function evalCFile( $file , $test_case_name , $time , $ext ){	
	
	if ( $ext == ".cpp"):
	
		$json = createCppExe( $file );
		
	endif;
	
	if ( $ext == ".c"):
	
		$json = createCExe( $file );
		
	endif;
	//$json = 
	if($json["error"] !== ""):
		
		return $json;
		
	endif;	
	
	$json = execExeFile( $test_case_name , $time);	
	
	return  $json;
}

// Esta funcion se encargara de recibir el nombre del archivo y el nombre del caso de prueba (que debe ser el mismo para .in y .out)

function newRun( $file , $test_name , $time ){

	
	
	/*
		VALIDAR LA EXTENSION SI NO ES UNA VALIDA REGRESA EL ERROR CORRESPONDIENTE
	*/
	
	
	$type = explode( "." , $file );
	
	$type = $type[count( $type )-1];
	
		
	if( isset( $type ) ):
	
		if($type == "java"):
		
			$file = str_replace( ".".$type , "" , $file );
			
			echo compareOut( evalJavaFile( $file , $test_name , $time ) , $test_name );
			
		endif;
		
		if( $type == "cpp" ):					
			
			echo compareOut ( evalCFile( $file , $test_name , $time , ".cpp") , $test_name );
			
		endif;
		
		if( $type == "c" ):					
			
			echo compareOut( evalCFile( $file , $test_name , $time , ".c") , $test_name );
			
		endif;
		
	endif;
	
}

//compara la respuesta obtenida de la evaluacion con la respuestas del caso de prueba

function compareOut( $json , $test_name){
		
		if($json["error"] != "") return json_encode( $json );
		
		$out = file_get_contents( (__TEST_CASES__)."$test_name.out" ); 		
				
		$out = explode( "\r\n" , $out);
		
		$i = 0;
		
		$wlines = 0;
		
		foreach( $out as $line ):
			
			if( isset($json["line".$i] ) ) :
				
				if( $json["line".$i] !=  $line ):
			
					$json["wline".$wlines] = "Line $i :".$json["line".$i]."-$line";
					
					$wlines++;
					
				endif;
				
			endif;
			
			$i++;
			
		endforeach;		
		
		if( $i != $json["lines"]):
				
				$json["status"] = "error";
				
				$json["error"] = "more lines";				
								
		endif;
		
		if( $wlines > 0):
				
				$json["status"] = "error";
				
				$json["error"] = "wrong answer";				
								
		endif;
		
		$json["wlines"] = $wlines;
		
		$json["mlines"] =  $i - $json["lines"] ;
		
		return json_encode( $json );
		
}

//debe checar que no se este evaluando otro codigo, si no, que espere , checar el unlink
function StatusJuez(){
	// ver si esta evaluando
	return false;
	
}

	
?>